namespace cpputils
{

class file_formatter
{
public:
	file_formatter(FILE* SetFile = 0);
	FILE* get_file() const;
	void set_file(FILE* Val);
	bool is_reading() const;
	void set_reading();
	bool is_writing() const;
	void set_writing();
	void set_blanks(const string& Val);
	void set_delimiters(const string& Val);
	bool is_blank(int Ch) const
	{
		return Ch != EOF && Types[(unsigned char)Ch] == blank_char;
	}
	bool is_delimiter(int Ch) const
	{
		return Ch != EOF && Types[(unsigned char)Ch] == delimiter_char;
	}
	bool is_alpha(int Ch) const
	{
		return Ch != EOF && Types[(unsigned char)Ch] == alpha_char;
	}
	bool is_alphas(const string& S);
	char get()
	{
		int Ch = fgetc(File);
		if (Ch == EOF) format_error("Unexpected end-of-file");
		return Ch;
	}
	int peek()
	{
		int Ch = fgetc(File);
		if (Ch == EOF) check_error();
		else ungetc(Ch, File);
		return Ch;
	}
	void put(char Ch)
	{
		if (fputc(Ch, File) == EOF) error();
	}
	int peek_nonblank();
	bool eof();
	void finish();
	file_formatter& operator<<(char Ch);
	file_formatter& operator<<(const char* S);
	file_formatter& operator<<(const string& S);
	template<class T>
	file_formatter& operator>>(const T& Val)
	{
		assert(is_writing());
		format(*this, to_nonconst(Val));
		return *this;
	}
	template<class T>
	file_formatter& operator>>(T& Val)
	{
		format(*this, Val);
		return *this;
	}
	void error();
	void check_error();
	void format_error(const string& Msg);

private:
	enum char_type { alpha_char, blank_char, delimiter_char };

	FILE* File;
	bool Reading;
	array<char_type> Types;

	void expect_string(const char* S, int Len);
};

template<class T>
void format(file_formatter& Fmt, T& Val)
{
	Val.format(Fmt);
}

void format(file_formatter& Fmt, char& Ch);
void format(file_formatter& Fmt, string& S);
void format(file_formatter& Fmt, int& V);
void format(file_formatter& Fmt, unsigned int& V);
void format(file_formatter& Fmt, short& V);
void format(file_formatter& Fmt, unsigned short& V);
void format(file_formatter& Fmt, long & V);
void format(file_formatter& Fmt, unsigned long& V);
void format(file_formatter& Fmt, long long& V);
void format(file_formatter& Fmt, unsigned long long& V);
void format(file_formatter& Fmt, float& V);
void format(file_formatter& Fmt, double& V);

namespace file_formatter_impl
{
	inline bool is_std_blank(int Ch);
	template<class T>
	void format_int(file_formatter& Fmt, T& V, const char* Type);
	template<class T>
	void format_float(file_formatter& Fmt, T& V, const char* Type);
}

}
